import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;

public class MFrame extends JFrame implements Runnable, KeyListener, MouseListener {
    int pwidth = Toolkit.getDefaultToolkit().getScreenSize().width;
    int pheight = Toolkit.getDefaultToolkit().getScreenSize().height;
    int height;
    int width;
    boolean isAI;
    int model;
    int SELECT;
    boolean onClick=false;
    public static final int OFFSET_X = 50;
    public static final int OFFSET_Y = 50;
    Sand_table sand_table,continue_game=null;
    boolean change;
    int bln=0;
    Thread snakeT=null;
    ArrayList<Sand_map> maps=new ArrayList<Sand_map>();
    Thread t = new Thread(this);
    public MFrame() {
        width = 700;
        height = 700;
        change = false;
        model=0;
        maps= (ArrayList<Sand_map>) Save.read("maps");
        sand_table= (Sand_table) Save.read("sand_table");
        continue_game=(Sand_table) Save.read("continue_game");
        this.setTitle("贪吃蛇");
        // 设置窗体大小
        this.setSize(width, height);
        // 设置窗体出现位置
        this.setLocation((pwidth - width) / 2, (pheight - height) / 2);
        // 将窗体设置为大小不可改变
        this.setResizable(false);
        // 将窗体的关闭方式设置为默认关闭后程序结束
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.addKeyListener(this);
        this.setBackground(Color.BLACK);
        this.setVisible(true);
        this.addMouseListener(this);
        this.addMouseMotionListener(mouseMotionListener);
        this.setVisible(true);
        sand_table = new Sand_table(30, 30);
        t.start();
        this.repaint();
        /*

        sand_table = new Sand_table(30, 30);
        isAI=false;
        this.repaint();
        new Thread(() -> {
            {
                while (sand_table.Move()) {
                    //if(sand_table.isAI)break;
                    change = false;
                    try {

                        Thread.sleep(sand_table.speed);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                stop();
            }
        }).start();
         */
    }

    public void stop() {
        JOptionPane.showMessageDialog(this, "游戏结束,您的最终长度为" + sand_table.snake.size() + ((sand_table.snake.size()>=sand_table.width*sand_table.height-2)?"":"  您真短~"));
        model=0;
        continue_game=null;
        mysave();
    }

    /***
     * 使用了双缓冲技术防止屏幕闪烁
     * @param g
     */
    boolean isInit=false;
    public void paint(Graphics g) {
        if(model!=3)isInit=false;
        if(model==0)//游戏未开始
        {
            selectModel(g);
            return;
        }
        if(model==1)//选择游戏
        {
            sand_table=new Sand_table(30,30);
            selectMap(g);
            return;
        }
        if(model==2)//关卡自定义
        {
            makeMap(g);
            return;
        }
        if(model==3)//继续游戏
        {
            if(!isInit)
            {
                isInit=true;
                for(Body_joint body_joint:sand_table.snake)
                {
                    sand_table.table[body_joint.getX()][body_joint.getY()]=1;
                }
                boolean isFood=false;
                for (int i = 0; i < sand_table.height&&!isFood; i++) {
                    for (int j = 0; j < sand_table.width&&!isFood; j++) {
                        if(sand_table.table[i][j]==2)isFood=true;
                    }
                }
                if(!isFood)sand_table.NewFood();
                snakeT=new Thread(() -> {
                    {
                        boolean isFail=true;
                        while (sand_table.Move()) {
                            //if(sand_table.isAI)break;
                            change = false;
                            try {

                                Thread.sleep(sand_table.speed);
                            } catch (InterruptedException e1) {
                                e1.printStackTrace();
                            }
                            if(model!=3)
                            {
                                isFail=false;
                                break;
                            }
                        }
                        if (isFail)stop();
                    }
                });
                snakeT.start();
            }
            playGame(g);
            return;
        }

    }
    public void selectModel(Graphics g)//model=0
    {
        BufferedImage bi = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics g2 = bi.createGraphics();
        g2.setColor(Color.LIGHT_GRAY);
        g2.setColor(Color.WHITE);
        g2.setFont(new Font("宋体", Font.BOLD, 30));
        g2.drawString("贪吃蛇游戏", OFFSET_X + 60, OFFSET_Y + 20);
        if (SELECT == 1) g2.setColor(Color.MAGENTA);
        g2.setFont(new Font("宋体", Font.BOLD, 30));
        g2.drawString("1.开始游戏", OFFSET_X + 60, OFFSET_Y + 120);
        if (SELECT == 2) g2.setColor(Color.MAGENTA);
        else g2.setColor(Color.WHITE);
        g2.setFont(new Font("宋体", Font.BOLD, 30));
        g2.drawString("2.自定义关卡", OFFSET_X + 60, OFFSET_Y + 160);

        if (SELECT == 3) g2.setColor(Color.MAGENTA);
        else g2.setColor(Color.WHITE);
        g2.setFont(new Font("宋体", Font.BOLD, 30));
        g2.drawString("3.继续游戏", OFFSET_X + 60, OFFSET_Y + 200);
        g.drawImage(bi, 0, 0, this);
        return;
    }
    int maps_index=0;
    public void selectMap(Graphics g)//model=1
    {
        BufferedImage bi = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics g2 = bi.createGraphics();
        g2.setColor(Color.LIGHT_GRAY);
        g2.fillRect(0, 0, width, height);
        g2.setColor(Color.BLACK);
        g2.fillRect(OFFSET_X, OFFSET_Y, sand_table.width * 20, sand_table.height * 20);

        g2.setColor(Color.LIGHT_GRAY);
        g2.fillRect(0, 0, width, height);
        int index=maps_index*4;
        if(index<maps.size())drawMap(g2,maps.get(index++),8,OFFSET_X,OFFSET_Y,SELECT_MAP==0);
        if(index<maps.size())drawMap(g2,maps.get(index++),8,OFFSET_X,OFFSET_Y+300,SELECT_MAP==1);
        if(index<maps.size())drawMap(g2,maps.get(index++),8,OFFSET_X+300,OFFSET_Y,SELECT_MAP==2);
        if(index<maps.size())drawMap(g2,maps.get(index++),8,OFFSET_X+300,OFFSET_Y+300,SELECT_MAP==3);
        g2.setColor(Color.GREEN);
        g2.fillRect(OFFSET_Y + sand_table.snake.getFirst().getY() * 20+1, OFFSET_X + sand_table.snake.getFirst().getX() * 20+1, 18, 18);
        g2.setColor(Color.green);
        g2.setFont(new Font("楷體",Font.PLAIN,20));
        String ym=(maps_index+1)+"/"+((maps.size()-1)/4+1);
        g2.drawString("ESC返回初始菜单,按动左右方向键翻页("+ym+")",OFFSET_Y+sand_table.width*20/2-100,OFFSET_X+sand_table.height*20+40);
        g.drawImage(bi, 0, 0, this);
        return;
    }
    public void makeMap(Graphics g)//model=2
    {
        BufferedImage bi = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics g2 = bi.createGraphics();
        g2.setColor(Color.LIGHT_GRAY);
        g2.fillRect(0, 0, width, height);
        g2.setColor(Color.BLACK);
        g2.fillRect(OFFSET_X, OFFSET_Y, sand_table.width * 20, sand_table.height * 20);

        drawMap(g2,sand_table);
        g2.setColor(Color.GREEN);
        g2.fillRect(OFFSET_Y + sand_table.snake.getFirst().getY() * 20+1, OFFSET_X + sand_table.snake.getFirst().getX() * 20+1, 18, 18);
        g2.setColor(Color.green);
        g2.setFont(new Font("楷體",Font.PLAIN,20));
        g2.drawString("ESC返回初始菜单，回车保存地图",OFFSET_Y+sand_table.width*20/2-100,OFFSET_X+sand_table.height*20+40);
        g.drawImage(bi, 0, 0, this);
        return;
    }
    public void playGame(Graphics g)
    {
        BufferedImage bi = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        Graphics g2 = bi.createGraphics();
        drawMap(g2,sand_table);
        g2.setColor(Color.GREEN);
        g2.fillRect(OFFSET_Y + sand_table.snake.getFirst().getY() * 20+1, OFFSET_X + sand_table.snake.getFirst().getX() * 20+1, 18, 18);
        g2.setColor(Color.green);
        g2.setFont(new Font("楷體",Font.PLAIN,20));
        g2.drawString("Snake速度: "+100000/sand_table.speed/100.0+"格/s,按A或D加减速，空格激活智能模式",OFFSET_Y+sand_table.width*20/2-250,OFFSET_X+sand_table.height*20+40);
        g.drawImage(bi, 0, 0, this);
    }
    boolean ttt=true;
    public void drawMap(Graphics g,Sand_table sand_table)
    {
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(0, 0, width, height);
        g.setColor(Color.BLACK);
        g.fillRect(OFFSET_X, OFFSET_Y, sand_table.width * 20, sand_table.height * 20);
        for (int i = 0; i < sand_table.height; i++) {
            for (int j = 0; j < sand_table.width; j++) {
                if (sand_table.table[i][j] == 1) {
                    g.setColor(Color.WHITE);
                    g.fillRect(OFFSET_Y + j * 20+1, OFFSET_X + i * 20+1, 18, 18);
                } else if (sand_table.table[i][j] == 2) {
                    g.setColor(Color.CYAN);
                    g.fillOval(OFFSET_Y + j * 20+1, OFFSET_X + i * 20+1, 18, 18);
                }else if (sand_table.table[i][j] == 3) {
                    g.setColor(Color.LIGHT_GRAY);
                    g.fillRect(OFFSET_Y + j * 20, OFFSET_X + i * 20, 20, 20);
                }
            }
        }
    }
    public void drawMap(Graphics g,Sand_map sand_map,int size,int OFFSET_X,int OFFSET_Y,boolean isSur)
    {
        if(isSur)
        {
            g.setColor(Color.RED);
            g.fillRect(OFFSET_X-1, OFFSET_Y-1, sand_map.width * size+2, sand_map.height * size+2);
        }
        g.setColor(Color.BLACK);
        g.fillRect(OFFSET_X, OFFSET_Y, sand_map.width * size, sand_map.height * size);
        for (int i = 0; i < sand_map.height; i++) {
            for (int j = 0; j < sand_map.width; j++) {
                if (sand_map.map[i][j] == 1) {
                    g.setColor(Color.WHITE);
                    g.fillRect(OFFSET_X + j * size, OFFSET_Y + i * size, size, size);
                } else if (sand_map.map[i][j] == 2) {
                    g.setColor(Color.CYAN);
                    g.fillOval(OFFSET_X + j * size, OFFSET_Y + i * size, size, size);
                }else if (sand_map.map[i][j] == 3) {
                    g.setColor(Color.WHITE);
                    g.fillRect(OFFSET_X + j * size, OFFSET_Y + i * size, size, size);
                }
            }
        }
    }
    public void run() {
        // TODO Auto-generated method stub
        while (true) {
            this.repaint();
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
    void mysave()
    {
        Save.save(sand_table,"sand_table");
        Save.save(sand_table,"continue_game");
        Save.save(maps,"maps");
    }
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode()==KeyEvent.VK_ESCAPE)
        {
            if(model==3)continue_game=sand_table;
            model=0;
            mysave();
            return;
        }
        if(model==2)
        {
            if(e.getKeyCode()==KeyEvent.VK_ENTER)
            {
                model=0;
                Sand_map mapt=new Sand_map(sand_table.width,sand_table.height);
                for (int i = 0; i < sand_table.height; i++) {
                    for (int j = 0; j < sand_table.width; j++) {
                        if(sand_table.table[i][j]==3)
                        {
                            mapt.map[i][j]=3;
                            sand_table.table[i][j]=0;
                        }
                    }
                }
                maps.add(mapt);
                mysave();
            }
            return;
        }
        if(model==1)
        {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                maps_index++;

            } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                maps_index--;
            }
            if(maps_index<0)maps_index=0;
            if(maps_index>(maps.size()-1)/4)maps_index=(maps.size()-1)/4;
            return;
        }
        if(e.getKeyCode()==KeyEvent.VK_SPACE)
        {
            sand_table.isAI=!sand_table.isAI;
            return;
        }
        if(e.getKeyCode()==KeyEvent.VK_A)
        {
            sand_table.decSpeed();
            return;
        }
        if(e.getKeyCode()==KeyEvent.VK_D)
        {
            sand_table.addSpeed();
            return;
        }
        if (change) return;
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            if (sand_table.dx == -1) return;
            sand_table.dx = 1;
            sand_table.dy = 0;
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {
            if (sand_table.dx == 1) return;
            sand_table.dx = -1;
            sand_table.dy = 0;
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            if (sand_table.dy == -1) return;
            sand_table.dx = 0;
            sand_table.dy = 1;
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            if (sand_table.dy == 1) return;
            sand_table.dx = 0;
            sand_table.dy = -1;
        }
        change = true;
    }
    private void repa() {

        this.repaint();
    }
    /***
     * 鼠标移动事件监听
     * 画预选信息
     */
    int SELECT_MAP=-1;
    private MouseMotionListener mouseMotionListener = new MouseMotionAdapter() {
        public void mouseMoved(MouseEvent e) {
            int x = e.getX();
            int y = e.getY();
            if (model == 0) {
                int xz;
                if (x > OFFSET_X + 60 && y > OFFSET_Y + 80 && x < OFFSET_X + 260 && y < OFFSET_Y + 120) xz = 1;
                else if (x > OFFSET_X + 60 && y > OFFSET_Y + 120 && x < OFFSET_X + 260 && y < OFFSET_Y + 160) xz = 2;
                else if (x > OFFSET_X + 60 && y > OFFSET_Y + 160 && x < OFFSET_X + 260 && y < OFFSET_Y + 200) xz = 3;
                else xz = -1;
                if (xz != SELECT) {
                    SELECT = xz;
                    repa();
                }
                return;
            }
            if(model==1)
            {
                int xz=-1;
                if(x>50&&x<288)
                {
                    if(y>50&&y<290)xz=0;
                    else if(y>350&&y<590)xz=1;
                }
                else if(x>350&&x<590)
                {
                    if(y>50&&y<290)xz=2;
                    else if(y>350&&y<590)xz=3;
                }
                if(xz!=SELECT_MAP)
                {
                    SELECT_MAP=xz;
                    repa();
                }
            }

        }
    };


    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if(e.getButton()==e.BUTTON1)
        {
            mysave();
            if(model==0&&SELECT>0)
            {
                model=SELECT;
                if(model==1)
                {
                    int x = e.getX();
                    int y = e.getY();
                    int xz=-1;
                    if(x>50&&x<288)
                    {
                        if(y>50&&y<290)xz=0;
                        else if(y>350&&y<590)xz=1;
                    }
                    else if(x>350&&x<590)
                    {
                        if(y>50&&y<290)xz=2;
                        else if(y>350&&y<590)xz=3;
                    }
                    if(xz!=SELECT_MAP)
                    {
                        SELECT_MAP=xz;
                        repa();
                    }
                }
                if(model==2)
                {
                    continue_game=sand_table;
                    sand_table = new Sand_table(30, 30);
                    bln=0;
                }
                if(model==3)
                {
                    if(continue_game==null)
                    {
                        model=0;
                        return;
                    }
                    sand_table=continue_game;
                    bln=sand_table.bln;
                }
                return;
            }
            if (model == 1)
            {
                if(SELECT_MAP<0)return;
                sand_table = new Sand_table(30, 30);
                Sand_map sand_map=maps.get(maps_index*4+SELECT_MAP);
                bln=0;
                for (int i = 0; i < sand_table.height; i++) {
                    for (int j = 0; j < sand_table.width; j++) {
                        sand_table.table[i][j]=sand_map.map[i][j];
                        if(sand_map.map[i][j]==3)bln++;
                    }
                }
                sand_table.bln=bln;
                sand_table.isAI=false;
                model=3;
                repa();


            }
            if (model == 2) {
                int x = e.getX();
                int y = e.getY();
                int j=x-OFFSET_Y-1;
                j/=20;
                int i=y-OFFSET_X-1;
                i/=20;
                if(sand_table.table[i][j]==0)sand_table.table[i][j]=3;
                else if(sand_table.table[i][j]==3)sand_table.table[i][j]=0;
            }
        }
        if(e.getButton()==e.BUTTON3)
        {
            if(model==1)
            {
                maps.remove(maps_index*4+SELECT_MAP);
            }
        }

    }

    @Override
    public void mousePressed(MouseEvent e) {
        if(e.getButton()==e.BUTTON1)
            onClick=true;
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if(e.getButton()==e.BUTTON1)
            onClick=false;

    }

    @Override
    public void mouseEntered(MouseEvent e) {

    }

    @Override
    public void mouseExited(MouseEvent e) {

    }
}